/* User code */
package parser;

import java_cup.runtime.*;
import java.io.*;
import java.math.BigInteger;

%%

%implements java_cup.runtime.Scanner
%type java_cup.runtime.Symbol


%eof{

%eof}


%eofclose
    
%{

static final int MAX_INDENT = 50;

int levels;
int[] spaces = new int[MAX_INDENT];
int level = 0;
int indentation;
int dedents = 0;
int linenumber = 1;

java_cup.runtime.Symbol symbol(int kind) {
    return new java_cup.runtime.Symbol(kind, linenumber, 0);
}

java_cup.runtime.Symbol symbol(int kind, Object value) {
    return new java_cup.runtime.Symbol(kind, linenumber, 0, value);
}

public java_cup.runtime.Symbol next_token() throws java.io.IOException {
    if (dedents > 0) {
        dedents--;
        return symbol(sym.DEDENT);
    }
    return yylex();
}

public static void main(String[] args) throws IOException {
    Yylex lexer = new Yylex(new FileInputStream(args[0]));
    Symbol s;
    do {
        s = lexer.next_token();
        String name = lexer.yytext();
        if (name.endsWith("\n"))
            name = "\n";
        System.out.println(s + " " + lexer.yytext());
    } while(s.sym != 0);
}

java_cup.runtime.Symbol doindent(boolean pushback) {
    if (pushback) 
        yypushback(1);
    yybegin(YYINITIAL);
    if (spaces[level] < indentation) {
        level++;
        spaces[level] = indentation;
        return symbol(sym.INDENT);
    }
    if (spaces[level] > indentation) {
        do { 
            level--;
            dedents++;
            if (spaces[level] < indentation) {
                return symbol(sym.ILLEGAL_INDENT);
            }
        } while (spaces[level] > indentation);
        dedents--;
        return symbol(sym.DEDENT);
    }
    return null;
}

%}

%state INDENT

%%                              

<YYINITIAL> {                             
    \~                     { return symbol(sym.TILDE); }
    \+                     { return symbol(sym.PLUS ); }
    \-                     { return symbol(sym.MINUS); }
    \*                     { return symbol(sym.MULT); }
    \*\*                   { return symbol(sym.POWER); }
    %                      { return symbol(sym.REM ); }
    \/                     { return symbol(sym.DIV); }
    ,                      { return symbol(sym.COMMA); }
    =                      { return symbol(sym.ASSIGN); }
    \"\"\"(.|\n)*\"\"\"         { return symbol(sym.STRING, yytext().substring(3, yytext().length()-3)); }
    \"[^\"\n]*\"           { return symbol(sym.STRING, yytext().substring(1, yytext().length()-1)); }
    \'[^\'\n]*\'           { return symbol(sym.STRING, yytext().substring(1, yytext().length()-1)); }
    [0-9]*\.[0-9]+([eE]-?[0-9]+)?  { return symbol(sym.FLOAT, Double.valueOf(yytext())); }
    [1-9][0-9]*            { return symbol(sym.INTEGER, new BigInteger(yytext())); }
    0[0-7]*                { return symbol(sym.INTEGER, new BigInteger(yytext(), 8)); }
    0[xX][0-9a-fA-F]+      { return symbol(sym.INTEGER, new BigInteger(yytext().substring(2), 16)); }
    \(                     { return symbol(sym.L_PAREN); }
    \)                     { return symbol(sym.R_PAREN); }
    \[                     { return symbol(sym.L_BRACKET); }
    \]                     { return symbol(sym.R_BRACKET); }
    \:                     { return symbol(sym.COLON); }
    \.                     { return symbol(sym.DOT); }
    \n                     { yybegin(INDENT); indentation = 0; linenumber++; return symbol(sym.NEWLINE, new Integer(linenumber)); }
    and                    { return symbol(sym.AND); }
    or                     { return symbol(sym.OR); }
    not                    { return symbol(sym.NOT); }
    \>                     { return symbol(sym.GT); }
    \>\=                   { return symbol(sym.GE); }
    \<                     { return symbol(sym.LT); }
    \<\=                   { return symbol(sym.LE); }
    \=\=                   { return symbol(sym.EQ); }
    \!\=                   { return symbol(sym.NE); }
    def                    { return symbol(sym.DEF); }
    class                  { return symbol(sym.CLASS); }
    in                     { return symbol(sym.IN); }
    return                 { return symbol(sym.RETURN); }
    if                     { return symbol(sym.IF); }
    else                   { return symbol(sym.ELSE); }
    elif                   { return symbol(sym.ELIF); }
    then                   { return symbol(sym.THEN); }
    while                  { return symbol(sym.WHILE); }
    for                    { return symbol(sym.FOR);  }
    import                 { return symbol(sym.IMPORT); }
    print                  { return symbol(sym.PRINT); }
    true                   { return symbol(sym.TRUE);  }
    try                    { return symbol(sym.TRY);  }
    except                 { return symbol(sym.EXCEPT);  }
    raise                  { return symbol(sym.RAISE);  }
    native                 { return symbol(sym.NATIVE);  }
    false                  { return symbol(sym.FALSE); }
    pass                   { return symbol(sym.PASS);  }
    [a-zA-Z_][a-zA-Z_0-9]* { return symbol(sym.IDENTIFIER, yytext()); }
    [ \t]+                 { }
    \\[ \t]*\n             { linenumber++; }
    #.*\n                  { linenumber++; }
    .                      { return symbol(sym.ILLEGAL_CHARACTER); }
}

<INDENT> {
    \n             { /* Ignore empty line */ indentation = 0; linenumber++; }
    [^ \t]         { java_cup.runtime.Symbol s = doindent(true); if (s != null) return s; }
    [ ]            { indentation++;   }
    [\t]           { indentation += 8; }
    <<EOF>>        { java_cup.runtime.Symbol s = doindent(false); if (s != null) return s; else return new java_cup.runtime.Symbol(sym.EOF); }
}


<<EOF>> { if (dedents > 0) {
        dedents--;
        return symbol(sym.DEDENT);
    } 
  return symbol(sym.EOF); 
}
